﻿/*
 * Restricciones: todas las mini imágenes tienen que medir lo mismo.
 */
class Galeria {
	// Los comentarios sobre my_xml en notación XPath son en base al modelo de referencia de fotos.xml, que acompaña la distribución estandar de este componente.
	private var my_xml:XML;   // archivo de configuración de las galerías
	private var imagen_seccion_mcl, imagen_boton_mcl, imagen_mcl:MovieClipLoader;
	
	// auxiliary to not to get a compiler error
	private var Galeria_96422_ref:Galeria;
	// auxiliaries that resembles receptor's children
	private var boton_seccion:MovieClip;
	// we store buttons in an array
	private var boton_seccion_array:Array;
	private var boton_scroll_disabled:Boolean=false;
	
	private var	seccion_actual = 1,   // el número de sección actual
	imagen_actual,   // el número de imagen actual (inicializado en 1 por defecto)
	imagenes_seccion_actual, // cantidad de imágenes de la sección actual
	pagina:Number;   // control de cambio de página de los botones-imágenes / mini imágenes, basado en 0, esto es: {0: página 1, 1: página 2, etc.}
	
	// Controlar qué sucede 1ero.:
	// El usuario setea la propiedad seccion (no es obligatorio que suceda).
	// Se ejecuta el cambio de sección inicial.
	private var cambio_seccion_inicial, cambio_imagen_inicial:Boolean;  // flag para controlar si el usuario realizó el cambio de sección inicial.
	
	// disposición gráfica de las mini imágenes
	private var filas = 4, columnas:Number = 2;
	private var origen_mini_imagen_x, origen_mini_imagen_y:Number;
	
	private var receptor:Object;
	
	// alias
	public static var wrap:Function = aplicar;
	
	public static function aplicar(receptor:Object):Object {
		//trace("Galeria.aplicar");
		// For some compilers that force to asign new objects to a variable.
		var galeria:Galeria = new Galeria(receptor);
		return receptor;
	}

	public function Galeria(receptor:Object) {
		EventDispatcherWrapper.aplicar(receptor);
		
		receptor.Galeria_96422_ref = this;
		receptor.addProperty("numero_seccion", get_seccion, set_seccion);
		receptor.addProperty("numero_imagen", get_imagen, set_imagen);
		receptor.addProperty("filas", get_filas, set_filas);
		receptor.addProperty("columnas", get_columnas, set_columnas);
		receptor.cargar = cargar;
		//receptor.crear_secciones = crear_secciones;
		
		// configuración inicial de la galería
		// ocultamiento de los objetos matriz
		receptor.boton_seccion._visible = false;
		receptor.botones_imagen.boton_imagen._visible = false;   // boton_imagen es el objeto matriz, luego, a partir de él se crean copias "boton_imagen_"+i
		
		// inicialización propia
		this.receptor = receptor;
		imagen_seccion_mcl = new MovieClipLoader();
		imagen_boton_mcl = new MovieClipLoader();
		imagen_mcl = new MovieClipLoader();
		var imagen_listener = {ref: this};
		imagen_mcl.addListener(imagen_listener);
		imagen_listener.onLoadInit = function(target_mc:MovieClip) {
			this.ref.notif_fin_carga_imagen();
		}
		origen_mini_imagen_x = receptor.botones_imagen._x;
		origen_mini_imagen_y = receptor.botones_imagen._y;
		
		// código del botón adelante
		Boton.aplicar(receptor.adelante);
		receptor.adelante.addEventListener2("release", this, "boton_scroll_release_2");
		// fin código del botón adelante
		
		// código del botón atrás
		Boton.aplicar(receptor.atras);
		receptor.atras.addEventListener2("release", this, "boton_scroll_release");
		// fin código del botón atrás

		// inicio de código de botón siguiente
		Boton.aplicar(receptor.siguiente);
		receptor.siguiente.addEventListener2("release", this, "boton_nav_release_2");
		// fin de código de botón siguiente
		// inicio de código de botón anterior
		Boton.aplicar(receptor.anterior);
		receptor.anterior.addEventListener2("release", this, "boton_nav_release");
		// fin de código de botón anterior
		
	}
	
	/* Method target: :Galeria
	 * event target: botón siguiente
	 */
	private function boton_nav_release_2(evt:Object):Void {
		// simular click en el botón siguiente
		receptor.numero_imagen = receptor.numero_imagen % imagenes_seccion_actual +1;
	}
	
	private function boton_nav_release(evt:Object):Void {
		// simular click en el botón anterior
		var anterior:Number= receptor.numero_imagen-1;
		if(anterior==0)
			anterior = imagenes_seccion_actual;
		receptor.numero_imagen = anterior;
	}
	
	/**
	 * method target: Galeria
	 * event target: boton_atras
	 */
	private function boton_scroll_release(evt:Object):Void {
		var boton = evt.target;
		pagina--;
		if(   pagina >= 0   ) {
			evt.type = "boton_scroll_release";
			evt.boton = evt.target;
			evt.target = receptor;
			evt.x = pagina * -(receptor.botones_imagen.boton_imagen_1._width*columnas) + origen_mini_imagen_x;
			evt.galeria = receptor;
			receptor.dispatchEvent(evt);

			// código por defecto en 2da. instancia
			if(boton_scroll_disabled) {
				boton_scroll_disabled = false;
				evt = {type: "activar_boton_scroll", target: receptor, galeria: receptor, boton: receptor.adelante};
				receptor.dispatchEvent(evt);
			}
		}
		else
		{
			pagina++;
		}
		if(    pagina == 0    )
		{
			boton_scroll_disabled = true;
			evt = {type: "desactivar_boton_scroll", target: receptor, galeria: receptor, boton: boton};
			receptor.dispatchEvent(evt);
		}
	}
	
	/**
	 * method target: Galeria
	 * event target: boton_adelante
	 */
	private function boton_scroll_release_2(evt:Object):Void {
		var boton = evt.target;
		pagina++;
		if(   pagina < Math.ceil(   imagenes_seccion_actual / (filas*columnas)  )    ) {
			evt.type = "boton_scroll_release";
			evt.boton = evt.target;
			evt.target = receptor;
			evt.x = pagina * -(receptor.botones_imagen.boton_imagen_1._width*columnas) + origen_mini_imagen_x;
			evt.galeria = receptor;
			receptor.dispatchEvent(evt);
			
			// código por defecto en 2da. instancia
			if(boton_scroll_disabled) {
				boton_scroll_disabled = false;
				evt = {type: "activar_boton_scroll", target: receptor, galeria: receptor, boton: receptor.atras};
				receptor.dispatchEvent(evt);
			}
		}
		else
		{
			pagina--;
		}
		if(    pagina == Math.ceil(  imagenes_seccion_actual / (filas*columnas)  ) - 1    )
		{
			boton_scroll_disabled = true;
			evt = {type: "desactivar_boton_scroll", target: receptor, galeria: receptor, boton: boton};
			receptor.dispatchEvent(evt);
		}
	}
	
	// target: receptor
	private function get_seccion():Number {
		return Galeria_96422_ref.seccion_actual;
	}
	
	// target: receptor
	private function set_seccion(seccion:Number):Void {
		Galeria_96422_ref.seccion_actual = seccion;
		// lógica de cambio de sección
		Galeria_96422_ref.cambiar_seccion();
	}
	
	// target: receptor
	private function get_imagen():Number {
		return Galeria_96422_ref.imagen_actual;
	}
	
	// target: receptor
	private function set_imagen(imagen:Number):Void {
		Galeria_96422_ref.imagen_actual = imagen;
		// lógica de cambio de sección
		Galeria_96422_ref.cambiar_imagen();
	}
	
	private function get_filas():Number {
		return Galeria_96422_ref.filas;
	}
	
	private function set_filas(filas:Number):Void {
		Galeria_96422_ref.filas = filas;
	}

	private function get_columnas():Number {
		return Galeria_96422_ref.columnas;
	}
	
	private function set_columnas(columnas:Number):Void {
		Galeria_96422_ref.columnas = columnas;
	}

	
	// target: receptor
	private function cargar(url_archivo:String):Void {
		//trace("Galeria.cargar, this: " + this + ", Galeria_96422_ref: " + Galeria_96422_ref + ".");
		// Observar condición de visibilidad de my_xml (recordar que está declarado en Galeria).
		Galeria_96422_ref.my_xml = new XML();
		Galeria_96422_ref.my_xml.ignoreWhite = true;
		Galeria_96422_ref.my_xml["Galeria_96422_ref"] = Galeria_96422_ref;
		Galeria_96422_ref.my_xml.onLoad = Galeria_96422_ref.xml_load;
		Galeria_96422_ref.my_xml.load(url_archivo);
		//trace("my_xml:" + Galeria_96422_ref.my_xml + ".");
	}
	
	// target: Galeria.my_xml
	private function xml_load(success):Void {
		trace("Galeria: archivo cargado");
		if (success)
		{
			Galeria_96422_ref.crear_secciones();
		}
	}
	
	/**
	 * Creación de las secciones. Implica crear los botones de cambio de sección,
	 * setearles imagen y texto, basado en el nodo "seccion"
	 * (ver XML de referencia).
	 * method target: Galeria
	 */
	private function crear_secciones() {
		// XPath: //galerias/seccion
		var nodo_seccion:XMLNode = my_xml.firstChild.firstChild;
		var i:Number;
		var init_object = {_y: receptor.boton_seccion._y};
		//trace("Nodo raíz: " + my_xml.firstChild + ".");
		boton_seccion_array = new Array();
		i=1;

		while(nodo_seccion!=null) {
			//trace("Creación de seccion: " + nodo_seccion.nodeName + ".");
			var boton_seccion = receptor.boton_seccion.duplicateMovieClip("boton_seccion_"+i, receptor.getNextHighestDepth(), init_object);
			// TODO cambiar a un componente de botón
			Boton.aplicar(boton_seccion);
			//boton_seccion.valor = i;
			// holds boton_seccion elements in creation order
			boton_seccion_array.push(boton_seccion);
			boton_seccion.addEventListener2("release", this, "boton_seccion_release");
			//boton_seccion.onRelease = function() {
			//	this._parent.numero_seccion = this.valor;
			//}
			// XPath: //galerias/seccion/boton-seccion@imagen
			imagen_seccion_mcl.loadClip(nodo_seccion.firstChild.attributes.imagen, boton_seccion.imagen)
			boton_seccion.titulo.texto.html = true;
			// XPath: //galerias/seccion/boton-seccion/(nodo de texto).value
			boton_seccion.titulo.texto.htmlText = nodo_seccion.firstChild.firstChild.nodeValue;
			init_object._y += 70;
			i++;
			nodo_seccion = nodo_seccion.nextSibling;
		}
		var evt = {type: "fin_creacion_secciones", target: receptor};
		evt.galeria = receptor;
		cambio_seccion_inicial = false;
		// Una posible poscondición de este comando es que cambio_seccion_inicial==true (depende de lo que haga el usuario en la detección del evento).
		receptor.dispatchEvent(evt);
		if(!cambio_seccion_inicial) {
			trace("Galeria - el usuario no cambió de sección.");
			cambiar_seccion();   // Punto de control: de ser necesario moverlo dentro de un detector de evento interno (para permitir que la detección del evento "fin_creacion_secciones" suceda 1ero.).
		}
	}
	
	/* method target: Galería
	 * event target: boton_seccion
	 */
	private function boton_seccion_release(evt:Object) {
		// find element's position in the array
		var i:Number = 0;
		while(i<boton_seccion_array.length && boton_seccion_array[i]!=evt.target)
			i++;
		trace("boton_seccion_release, i: " + i + ".");
		// use the found position to change numero_seccion (remember that boton_seccion elements are stored in creation order, which in turn represents section number).
		if(i<boton_seccion_array.length)
			receptor.numero_seccion = i+1;
		// redifusión del evento, event bubbling
		evt.boton = evt.target;
		evt.target = receptor;
		evt.galeria = receptor;
		evt.type = "boton_seccion_release";
		receptor.dispatchEvent(evt);
	}
	
	/**
	 * Cambio de sección. Implica crear los botones de selección de imagen,
	 * setear una imagen inicial para mostrar, basado en el nodo "boton"
	 * (ver XML de referencia).
	 * target: Galeria
	 */
	private function cambiar_seccion() {
		trace("Galeria.cambiar_seccion, cambio de sección, seccion: " + seccion_actual + ".");
		cambio_seccion_inicial = true;
		// XPath: //galerias/seccion/titulo-seccion/(nodo de texto)@value
		var nodo_titulo_seccion:XMLNode = my_xml.firstChild.childNodes[seccion_actual-1].firstChild.nextSibling.firstChild;
		trace(receptor.titulo.texto);
		receptor.titulo_seccion.texto.html = true;
		// XPath: //galerias/seccion/titulo-seccion/(nodo de texto)@value
		receptor.titulo_seccion.texto.htmlText = nodo_titulo_seccion.nodeValue;
		
		// remover los MovieClip's de mini imágenes (buscar por nombre + núm. secuencia)
		var i:Number = 1;
		var boton_imagen:MovieClip;
		while(   (boton_imagen=receptor.botones_imagen["boton_imagen_"+i]) != undefined   ) {
			boton_imagen.removeMovieClip();
			i++;
		}
		
		// Creación de los botones de mini imágenes
		// duplicar el MovieClip de mini imágenes (nodos "boton-seccion" del XML).
		i = 0;
		// XPath: //galerias/seccion/boton
		var nodo_boton_imagen:XMLNode = my_xml.firstChild.childNodes[seccion_actual-1].firstChild.nextSibling.nextSibling;
		//trace("Galeria.cambiar_seccion, boton_imagen: " + boton_imagen + ".");
		while(nodo_boton_imagen!=null) {
			// Recordar que boton_imagen es el objeto matriz (buscar más arriba el comentario por palabra clave "matriz").
			boton_imagen = receptor.botones_imagen.boton_imagen.duplicateMovieClip("boton_imagen_"+(i+1), receptor.botones_imagen.getNextHighestDepth());
			
			// cargar las mini imágenes del XML dentro de MovieClip's, con el texto descriptivo
			// XPath: //galerias/seccion/boton@imagen
			imagen_boton_mcl.loadClip(nodo_boton_imagen.attributes.imagen, boton_imagen.imagen);

			boton_imagen.titulo.texto.html = true;
			// XPath: //galerias/seccion/boton/(nodo de texto)@value
			boton_imagen.titulo.texto.htmlText = nodo_boton_imagen.firstChild.firstChild.nodeValue;
			// TODO usar un componente de botón
			boton_imagen.valor = i+1;
			boton_imagen.area_mouse.onRelease = function() {
				this._parent._parent._parent.numero_imagen = this._parent.valor;
			}
			
			/* documentación de disposición en grillas, filas y columnas
			 *
			 * dada una secuencia de elementos [1, 2, 3, ..., n]
			 * se disponen en grillas, filas y columnas, en orden ascendente
			 * (de particular a general):
			 * 1ero. por fila,
			 * 2do. por columna y
			 * 3ro. por grilla
			 *
			 * Grillas:
			 * -------------    -------------    -------------    -------------    
			 * |           |    |           |    |           |    |           |    
			 * |           |    |           |    |           |    |           |    
			 * |           |    |           |    |           |    |           |    
			 * |           |    |           |    |           |    |           |    
			 * -------------    -------------    -------------    -------------    
			 * Fig.: cada uno de los rectángulos representa 1 grilla, con la misma canti
			 * dad de filas y columnas que los otros, salvo el último que puede tener menos filas,
			 * si la cantidad de fotos no es múltiplo de FOTOS_X_PAG
			 *
			 * la disposición de las grillas es horizontal
			 * Es decir: cada grilla está a la derecha de su anterior
			 *
			 * Llenado de grillas:
			 * Se llena una fila, pasa a la fila siguiente
			 * Se llena una grilla, pasa a la grilla siguiente
			 */
			 
			/* Punto de inserción de disposición en grillas, filas y columnas
			 * para modificar la disposición de filas y columnas 
			 * modificar las fórmulas donde se define fila y columna,
			 * además ir a la sección "documentación de disposición en grillas, filas y columnas"
			 * y modificar la explicación de cómo está armado
			 */
			// cálculo de la fila y columna correspondiente al botón actual
			var fila:Number = Math.floor(i / (filas * columnas /*fotos_x_pagina*/) ) * columnas + i % columnas;
			var columna:Number = Math.floor(i/columnas) % filas;
			
			// transformación a coordenadas reales (pixels), a partir de las coordenadas virtuales (fila, columna)
			// el uso de boton_imagen en el cálculo de las coordenadas reales es en parte lo que restringe que todas las mini imágenes tengan que medir lo mismo.
			boton_imagen._x = fila * boton_imagen._width;   // ancho de la mini foto (contemplar una brecha, o indicarle al usuario que inserte un área transparente para dejar una brecha)
			boton_imagen._y = columna * boton_imagen._height;   // alto de la mini foto

			i++;
			// XPath: //galerias/seccion/boton
			nodo_boton_imagen = nodo_boton_imagen.nextSibling.nextSibling;
		}   // fin de creación de botones de cambio de imagen
		
		imagenes_seccion_actual = i;
		
		// limita la visualización de los botones al área (de filas y columnas) de paginación
		// 1) recordar que no se puede usar la misma máscara para enmascarar más de 1 objeto (en tiempo de ejecución).
		// 2) recordar no se puede duplicar un MovieClip a una ruta de destino padre diferente de la del original.
		// por 1 y por 2 mantenemos todos los objetos clones de boton_imagen en botones_imagen
		
		// crear la máscara de cubrimiento de los botones
		// creación de la máscara de visualización de las filas y columnas de la página (en el sentido de paginación por filas y columnas)
		if(receptor.mascara_boton_imagen==undefined)
			receptor.createEmptyMovieClip("mascara_boton_imagen", receptor.getNextHighestDepth());   // ver más abajo "mascara_boton_imagen"

		
		// esto también genera la restricción de que todas las mini imágenes de botones
		// de cambio de imagen tengan que medir lo mismo.
		receptor.mascara_boton_imagen._x = origen_mini_imagen_x;
		receptor.mascara_boton_imagen._y = origen_mini_imagen_y;
		receptor.mascara_boton_imagen.clear();
		receptor.mascara_boton_imagen.lineStyle(1, 0, 100);   // thickness = 0
		receptor.mascara_boton_imagen.beginFill(0xFFFF00);
		receptor.mascara_boton_imagen.moveTo(0, 0);
		receptor.mascara_boton_imagen.lineTo(boton_imagen._width*columnas-1, 0);
		receptor.mascara_boton_imagen.lineTo(boton_imagen._width*columnas-1, boton_imagen._height*filas);
		receptor.mascara_boton_imagen.lineTo(0, boton_imagen._height*filas);
		receptor.mascara_boton_imagen.lineTo(0, 0);
		receptor.mascara_boton_imagen.endFill();
		receptor.botones_imagen.setMask(receptor.mascara_boton_imagen);
	
		// difundir el evento "fin_cambio_seccion"
		cambio_imagen_inicial = false;
		// Una posible poscondición de este comando es que cambio_imagen_inicial==true (depende de lo que haga el usuario en la detección del evento).
		receptor.dispatchEvent({type: "fin_cambio_seccion", target: receptor});
		if(!cambio_imagen_inicial) {
			/* Si el usuario deja que esta clase haga el cambio de imagen,
			 * hacemos un cambio de imagen predeterminado.
			 */
			pagina = 0;
			receptor.adelante.enabled = true;
			receptor.adelante._alpha = 100;
			receptor.atras.enabled = true;
			receptor.atras._alpha = 100;
			receptor.botones_imagen._x = origen_mini_imagen_x;
			receptor.numero_imagen = 1;   // Punto de control: de ser necesario moverlo dentro de un detector de evento interno (para permitir que la detección del evento "fin_cambio_seccion" suceda 1ero.).
		}
	}
	
	/**
	 * Cambio de imagen. Implica cambiar una imagen por otra,
	 * basado en el nodo "imagen-grande" (ver XML de referencia).
	 * target: Galeria
	 */
	private function cambiar_imagen() {
		trace("Galeria.cambiar_imagen, receptor.imagen: " + receptor.imagen + "(" + receptor + ").");
		cambio_imagen_inicial = true;
		// XPath: //galeria/seccion/imagen-grande
		var nodo_imagen:XMLNode = my_xml.firstChild.childNodes[seccion_actual-1].childNodes[imagen_actual*2+1];
		
		receptor.dispatchEvent({type: "ini_cambio_imagen", target: receptor, imagen: receptor.imagen});
		
		receptor.imagen.createEmptyMovieClip("carga_imagen", receptor.imagen.getNextHighestDepth());
		// XPath: //galeria/seccion/imagen-grande@imagen
		imagen_mcl.loadClip(nodo_imagen.attributes.imagen, receptor.imagen.carga_imagen);
		receptor.titulo.texto.html = true;
		// XPath: //galeria/seccion/imagen-grande/texto/(nodo de texto)@value
		receptor.titulo.texto.htmlText = nodo_imagen.firstChild.firstChild.nodeValue;
		
		// Evento no muy útil. Mejor usar "fin_carga_imagen".
		receptor.dispatchEvent({type: "fin_cambio_imagen", target: receptor, imagen: receptor.imagen});
	}
	
	/* La usa imagen_listener, para notificar que se terminó de cargar una imagen grande.
	 * 
	 */
	private function notif_fin_carga_imagen() {
		/* Usos conocidos / recomendados para detectar este evento:
		 * - Ponerle una transición de entrada a la imagen cargada.
		 * - Activar el suavizado de imagen.
		 */
		receptor.dispatchEvent({type: "fin_carga_imagen", target: receptor, imagen: receptor.imagen});
	}
	
	function dispatchEvent() {}
}

/* 
 * -> Establecer un patrón de medición de no detección de eventos.
 * En vez de eso lo que se pudo hacer fue establecer un patrón
 * para detectar si el usuario modificó o no un atributo, y proveer
 * un evento en cuyo Event Object (el objeto con información del evento)
 * se dé acceso a dicha propiedad.
 *
 * -> En base a lo anterior, programar acciones predeterminadas para:
 *     Cambio de página de mini imágenes.
 *     Desactivación de los botones de desplazamiento al alcanzar los topes. Con respecto a este punto:
 *     es muy complicado encontrar/definir un atributo para medir si el usuario lo modificó o no, ya que la desactivación
 *     de un botón se puede hacer incluso sin ninguna información del evento (objeto evento).
 *     Cambio de la imagen grande.
 *
 * -> Poner parámetros de ajustar automáticamente las imágenes y mini imágenes al área designada:
 *     - Circunscrito.
 *     - Inscrito.
 *     - Exacto (ancho y alto independientes enre sí).
 *
 * -> Usar componentes de botón para los botones.
 *
 * Bugs:
 * - La 1era. vez a veces no carga la imagen.
 * Revisar si se trata de un problema de dependencia de carga e inicialización,
 * Entre la imagen que se tiene que cargar y el contenedor donde se tiene que cargar.
 * Estado: solucionado. En realidad está mal la descripción del bug. Siempre
 * carga la imagen, el problema es que el contenedor de la imagen estaba creado con alpha=0,
 * y, a menos que la variable aleatoria provocara una transición a alpha=100, nunca cambiaba
 * el alpha=0. Por eso, cuando la transición inicial era alpha se veía bien, y las
 * subsiguientes veces, al estar solucionado el problema, la transición width funcionaba bien.
 */